package com.qust.endecryptionmanager;

import com.alibaba.fastjson2.JSON;
import com.qust.encryptionAndDecryption.enDecryption.AbstractEnDecryptionWithKeyPair;
import com.qust.endecryptionmanager.pojo.Configuration;
import com.qust.endecryptionmanager.pojo.ZkNode;
import com.qust.util.NettyUtils;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import lombok.extern.log4j.Log4j2;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CopyOnWriteArrayList;

@Log4j2
@SpringBootApplication
public class EnDecryptionManagerApplication {
    /**
     * 创建EventLoopGroup,为了创建netty channel
     * @return
     */
    @Bean(value = "EventLoopGroup")
    public EventLoopGroup getEventLoopGroup(){
        return new NioEventLoopGroup();
    }

    /**
     * 创建MessageHandle,只能有一个channel使用,为方便创建channel设置为Sharable
     * @return
     */
    @Bean(value = "MessageHandle")
    public NettyUtils.MessageHandle getMessageHandle(){
        return new NettyUtils.MessageHandle();
    }


    /**
     * 连接zookeeper,创建CuratorFramework
     * @param configuration 用来存储配置信息
     * @return
     * @throws Exception
     */
    @Bean(value = "zkCliBean")
    public CuratorFramework getZkClient(Configuration configuration) throws Exception {
        CuratorFramework zkClient = CuratorFrameworkFactory
                .builder()
                .connectString(configuration.getZkAddress())
                .sessionTimeoutMs(60 * 1000)  //会话超时时间
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(new ExponentialBackoffRetry(3000, 10))     //掉线重连策略
                .namespace("EnDecryption") //默认一个根目录，以后的所有创建都会在此目录下进行
                .build();
        zkClient.start();
        return zkClient;
    }

    /**
     * 添加对 /server子节点的监视,修改Configuration的属性,用于后续与server集群建立短连接
     * @param configuration
     * @param zkClient
     * @return
     * @throws Exception
     */
    @Bean(value = "zkWatchBean")
    @DependsOn({"zkCliBean","zkNodes"})
    public PathChildrenCache refresh(Configuration configuration, CuratorFramework zkClient,Map<String ,ZkNode> zkNodes) throws Exception {
        final int[] size = {zkClient.getChildren().forPath("/server").size()};
        PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient,"/server",false);
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
                String path = pathChildrenCacheEvent.getData().getPath();
                if (pathChildrenCacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED) && size[0] <= 1){
                    changeConfiguration(zkClient, configuration);
                    byte[] bytes = curatorFramework.getData().forPath(path);
                    ZkNode zkNode = JSON.parseObject(bytes).to(ZkNode.class);
                    zkNodes.put(path,zkNode);
                }else if (pathChildrenCacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)){
                    changeConfiguration(zkClient, configuration);

                    zkNodes.remove(path);
                }
                log.debug("configuration更改,{}",configuration);
                size[0] -=1;
            }
        });
        pathChildrenCache.start();
        return pathChildrenCache;
    }

    private void changeConfiguration(CuratorFramework zkClient, Configuration configuration) throws Exception {
        List<String> list = zkClient.getChildren().forPath("/server");
        list.sort((o1, o2) -> {
            int i1 = Integer.parseInt(o1.split(";")[1]);
            int i2 = Integer.parseInt(o2.split(";")[1]);
            return i1-i2;
        });
        if (!list.isEmpty()){
            String path = list.get(0).split(";")[0];

            log.debug(path);

            configuration.setServerPort(Integer.parseInt(path.split(":")[1]));
            configuration.setServerIp(path.split(":")[0]);
        }else {
            configuration.setServerPort(0);
            configuration.setServerIp(null);
        }
    }


    /**
     * 缓存算法实例
     * @return
     */
    @Bean(value = "EnDeMap")
    public Map<String , AbstractEnDecryptionWithKeyPair> getEnDeMap(){
        return new HashMap<>();
    }
    @Bean(value = "zkNodes")
    public Map<String ,ZkNode> getZkNodes(){
        return new ConcurrentHashMap<>();
    }


    public static void main(String[] args) {
        SpringApplication.run(EnDecryptionManagerApplication.class, args);
    }
}
